

import SSF.OS.*;
import SSF.Net.*;
import SSF.Net.Util.*;
import com.renesys.raceway.DML.*;

/** 
 *  Simplest demonstration of modeling CPU delay in handling 
 *  packets.   This is a really bad IP variant that makes each packet 
 *  wait for ten milliseconds before it's processed; the delay applies 
 *  to both inbound and outbound packets.  
 *
 *  In the associated demo ("make" to run), we enable slowIP on exactly 
 *  one router in campus2.dml so that we can watch the packets pile up.
 *  Uncomment the printlns below to make some verbose output .. then 
 *  weed through that output and watch the pattern of Enqueue...Send
 *  messages.   Or, for real evil fun, change the 0.01 second delay 
 *  below to something serious, like 10 seconds, and watch TCP die.
 *
 *  Someone, please, powercycle that router! 
 */
public class slowIP extends IP {

    /** If we just had push() call pushAfterDelay(), we would infinite-loop
     *  because pushAfterDelay() would eventually call push(), which would 
     *  call pushAfterDelay() .. and so on. 
     *
     *  Instead, we introduce this little carrier class so that we 
     *  can distinguish the two push cases (predelay and postdelay).
     *
     *  This is just a fun example.  In fact, most protocols should not 
     *  implement push in terms of pushWithDelay, both because of the 
     *  potential for accidental looping, and because the source (that is, the 
     *  significance, in addition to the magnitude) of the delay should be 
     *  determined by the caller of push().   The astute modeler will therefore
     *  call plain push() when she intends to ignore CPU contention, and 
     *  pushAfterDelay() when she intends to model CPU contention.
     *
     *  Put another way, delays may be imposed by protocols (including NICs)
     *  when they push information to each other.  Delays are not self-imposed
     *  by a protocol on itself (as in this example).  It's hard to give a 
     *  concise example; read the SSF.OS protocol code instead, grepping for 
     *  pushAfterDelay(). 
     */
    private class slowMessage extends ProtocolMessage {
	ProtocolMessage original;
	public slowMessage(ProtocolMessage original) {
	    this.original = original;
	}
    }

    /** In general, don't write push() this way in your own code.  
     * 
     *  See notes above. 
     */
    public boolean push(ProtocolMessage message, ProtocolSession fromSession) 
    throws ProtocolException {
	
	if (!(message instanceof slowMessage)) {  // A freshly pushed message!

	    System.err.println("Enqueue at "
	                       +inGraph().now()/(double)SSF.Net.Net.seconds(1.0)
	                       +":   "+message);
	    return pushAfterDelay(new slowMessage(message),fromSession,0.01);

	} else {   // A message we have pushed (after delay) to ourselves!

	    message = ((slowMessage)message).original;
	    System.err.println("   Send at "
	                       +inGraph().now()/(double)SSF.Net.Net.seconds(1.0)
	                       +":   "+message);
	
	    return super.push(message, fromSession);
	}
    }
}
